#ifndef _SHAREDCONSTBUFFER_HPP_
#define _SHAREDCONSTBUFFER_HPP_

#include <boost/cstdint.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>

    namespace network
    {
        namespace buffer
        {

            /** @fn				hexDump
                    @brief			Convenience c-style function for debugging, allow a human-readable print in the console
                    @param	_o		stream on which print result
                    @param  _data	data container to print
                    @return			no return
             */
            template<typename T>
            void hexDump(std::ostream &_o, const T& _data)
            {
                boost::uint16_t pos = 0;
                int lpos = 0;
                while (pos <= _data.size()) try
                    {
                        lpos = 0;
                        _o << std::hex;
                        _o.fill('0');
                        _o.width(8);
                        // this is here to trigger exception if we'r out of
                        // range already at this point
                        _data.at(pos);
                        _o << pos << " ";
                        for (boost::uint8_t i = 0; i < 8; ++i, ++lpos, pos++)
                        {
                            _o << " ";
                            int c = static_cast<int> (_data.at(pos));
                            if ((c < 0 ? c += 256 : c) < 16)
                            {
                                _o << "0";
                            }
                            _o << c;
                        }
                        _o << " ";
                        for (boost::uint8_t i = 0; i < 8; ++i, ++lpos, pos++)
                        {
                            _o << " ";
                            int c = static_cast<int> (_data.at(pos));
                            if ((c < 0 ? c += 256 : c) < 16)
                            {
                                _o << "0";
                            }
                            _o << c;
                        }
                        _o << "  ";
                        _o << "|";
                        pos -= 16;
                        for (boost::uint8_t i = 0; i < 16; ++i, ++pos)
                        {
                            boost::uint8_t c = _data.at(pos);
                            _o.put((c > 32 && c < 127) ? c : '.');
                        }
                        _o << "|" << std::endl;
                    }
                    catch (std::out_of_range &)
                    {
                        if (!(_data.size() % 16))
                        {
                            break;
                        }
                        int curpos = 12 + 3 * lpos;
                        if (lpos < 8)
                        {
                            --curpos;
                        }
                        for (boost::uint8_t i = 0; i < 60 - curpos; ++i)
                        {
                            _o << " ";
                        };
                        _o << " |";
                        pos -= lpos;
                        for (boost::uint8_t i = 0; i < lpos; ++i, ++pos)
                        {
                            boost::uint8_t c = _data.at(pos);
                            if (c > 32 && c < 127)
                            {
                                _o << c;
                            }
                            else
                            {
                                _o << ".";
                            }
                        }
                        _o << "|" << std::endl;
                        break;
                    }
                _o << std::dec;
            }



            /**
             *	@lang		French
             *	@class		SharedConstBuffer
             *	@brief		Buffer utilisÈ pour la constitution de packet.
             *				Il est composÈ d'un cont_buffer d'asio et d'un std::vector<char>
             *				allouÈ dynamiquement en fonction de la taille du packet
             *				InspirÈ du shared_const_buffer fourni dans les exemples d'asio
             */

            /** @lang		English
                    @class		SharedConstBuffer
                    @brief		Based Buffer used to construct packet.
                                            Composing with a const_buffer from asio and a vector
                                            of char from stl dynamicly allocated which the size
                                            depend on packet's size.
                                            Inspired by the shared_const_buffer provided by asio tutorials
             */
            class SharedConstBuffer
            {
            public:

                /** @typedef		value_type
                        @brief			needed by asio to get the buffer to send.
                 */
                typedef boost::asio::const_buffer value_type;
                /** @typedef		const_iterator
                        @brief			needed by asio to get the buffer to send.
                 */
                typedef const boost::asio::const_buffer* const_iterator;

                /** @fn				begin
                        @brief			allow getting a beginnig iterator on the data buffer
                                                        needed by asio to get the buffer to send.
                        @return			return an iterator on the beginning of the buffer
                 */
                const boost::asio::const_buffer* begin() const;
                /** @fn				end
                        @brief			allow getting an ending iterator on the data buffer
                                                        needed by asio to get the buffer to send.
                        @return			return an iterator on the end of the buffer
                 */
                const boost::asio::const_buffer* end() const;
                /** @fn				c_data
                        @brief			constant getter on the data containing by the buffer
                        @return			return a constant unsigned char pointer on the buffer's first element
                 */
                const unsigned char * c_data() const;
                /** @fn				data
                        @brief			getter on the data containig by the buffer
                        @return			return a pointer on the buffer's first unsigned char
                 */
                unsigned char* data() const;
                /** @fn				size
                        @brief			getter on the buffer total size
                        @return			return std::size_t representing the total size of the buffer
                 */
                const std::size_t size() const;

                /** @fn				SharedConstBuffer(std::size_t size)
                        @brief			Construct a buffer with an initial size.
                                                        It substitute to default constructor because
                                                        the size have to be defined at the construction
                        @param	_size	the size that the buffer will be construct with
                        @return			return an instance of SharedConstBuffer
                 */
                explicit SharedConstBuffer(std::size_t _size);
                /** @fn				SharedConstBuffer(boost::shared_ptr< std::vector< unsigned char > >& data, std::size_t size)
                        @brief			Reconstruct a buffer from a vector of unsigned char and a size. This constructor must only used in order to
                                                        reconstruct a packet from a sender, and not to build a packet to send.
                        @param	_data	The data buffer containing received information
                        @param	_size	The real size of the packet which is received
                        @return			Return an instance of SharedConstBuffer
                 */
                explicit SharedConstBuffer(boost::shared_ptr< std::vector< unsigned char > >& _data);
                /** @fn				SharedConstBuffer(boost::shared_ptr< std::vector< unsigned char > >& data, std::size_t size)
                        @brief			Reconstruct a buffer from a vector of unsigned char and a size. This constructor must only used in order to
                                                        reconstruct a packet from a sender, and not to build a packet to send.
                        @param	_data	The data buffer containing received information
                        @param	_size	The real size of the packet which is received
                        @return			Return an instance of SharedConstBuffer
                 */
                explicit SharedConstBuffer(boost::shared_ptr< std::vector< unsigned char > >& _data, boost::uint16_t _size);
                /** @fn				SharedConstBuffer(const shared_const_buffer& _data)
                        @brief			Copy constructor, construct a buffer with another instance of SharedConstBuffer.
                        @param	_copy	The reference on the instance of SharedConstBuffer to copy.
                        @return			return an instance of SharedConstBuffer
                 */
                explicit SharedConstBuffer(const SharedConstBuffer& _copy);
                /** @fn				~SharedConstBuffer()
                        @brief			virtual destructor
                        @return			no return
                 */
                virtual ~SharedConstBuffer();
                /**	@fn				put_char_at(unsigned char c, int offset)
                        @brief			The most low-level function, putting a simple unsign char in the buffer at the position offset
                        @param	_c		The unsigned char to put into the buffer
                        @param	_offset	The offset at which put the char
                        @return			no return
                 */
                void put_char_at(unsigned char _c, int _offset);

                /**	@fn				put(T _data)
                 *	@lang			French
                 *	@brief			Ecrit tout type de donnÈe primitive en swapant l'ordre des bits si necessaire.
                 *					Fonctionne avec les types donnÈes dans stdint.h
                 *	@param	_data	Les données à insérer dans le buffer.
                 *	@return			no return
                 */

                /**	@fn				put(T _data)
                        @lang			English
                        @brief			Allow the writing of any primitive type into the buffer.
                        @param	_data	Data to write into the buffer
                        @return			no return
                 */
                template<typename T>
                void put(T _data)
                {
                    BOOST_ASSERT((sizeof (T) + m_offset) <= m_size);
                    *((T*) (&((*m_data)[m_offset]))) = _data;
                    m_offset += sizeof (T);
                }

				template<typename T>
				void put( std::vector<T> _data)
                {
                    BOOST_ASSERT(( _data.size() + m_offset) <= m_size);
					for( std::vector< T >::iterator it_data = _data.begin(); it_data != _data.end(); it_data++ )
					{
						*((T*) (&((*m_data)[m_offset]))) = *it_data;
						m_offset += sizeof (T);
					}
                }

				/*template<>
				void put< RawData>( we::RawData _data)
                {
					m_size+=sizeof(boost::uint8_t) + sizeof( unsigned char ) * _data.size();
					BOOST_ASSERT(( _data.size() + sizeof( boost::uint8_t) + m_offset) <= m_size);
					boost::uint8_t size = _data.size();
					m_data->push_back(size);
					//*((boost::uint16_t*) (&((*m_data)[m_offset]))) = size;
					m_offset += sizeof (boost::uint8_t);
					for( std::vector< unsigned char >::iterator it_data = _data.begin(); it_data != _data.end(); it_data++ )
					{
						m_data->push_back( *it_data );
						m_offset += sizeof (unsigned char);
					}
					put_at_offset(&m_size, sizeof(boost::uint16_t),0);
                }

				template<>
				void put<we::Vector3>( we::Vector3 _data )
                {
					BOOST_ASSERT( ( sizeof(we::Real) * 3 + m_offset) <= m_size);
					memcpy( &((*m_data)[m_offset]),&_data.x,sizeof(we::Real));
					m_offset+=sizeof(we::Real);
					memcpy( &((*m_data)[m_offset]),&_data.y,sizeof(we::Real));
					m_offset+=sizeof(we::Real);
					memcpy( &((*m_data)[m_offset]),&_data.z,sizeof(we::Real));
					m_offset+=sizeof(we::Real);
				}*/

				template<typename T>
				void put( std::vector<T*> _data)
                {
                    BOOST_ASSERT(( _data.size() + m_offset) <= m_size);
					for( std::vector< T >::iterator it_data = _data.begin(); it_data != _data.end(); it_data++ )
					{
						*((T*) (&((*m_data)[m_offset]))) = *(*it_data);
						m_offset += sizeof (T);
					}
                }

                /**	@fn			put(std::string _data)
                 *	@lang		French
                 *	@brief		Ecrit std::string dans le buffer, en surchargeant la fonction template pour éviter
                 *				les types non primitifs dans la fonction template.
                 *	@param		std::string representant les données à insérer
                 *	@return		no return
                 */
                /**	@fn			put(std::string _data)
                 @lang			English
                 @brief			Write an std::string into the buffer, overloading the template function to avoir unprimitive type.
                 @param	_data	Data to write into the buffer
                 @return		no return
                 */
                void put(std::string _data);

                /**	@fn				put(std::vector<unsigned char*> _data)
                 *	@lang			French
                 *	@brief			Ecrit un vector<char> dans le buffer, en surchargeant la fonction template pour éviter
                 *					les types non primitifs dans la fonction template.
                 *	@param	_data	un pointeur sur un std::vector d'unsigned char représentant les données à écrire dans le buffer.
                 *	@return			rien à retourner
                 */
                /**	@fn				put(std::vector<unsigned char>* _data)
                        @lang			English
                        @brief			Write an std::vector into the buffer, overloading the template function to avoir unprimitive type.
                        @param	_data	Data to write into the buffer
                        @return			no return
                 */
                void put(std::vector<unsigned char>* _data);


                /**	@fn				put(const std::string& _data, size_t _size)
                 *	@lang			French
                 *	@brief			Ecrit un const std::string dans le buffer, en remplissant du ( _size - _data.size )eme octet jusqu'au ( _size )eme octet de 0.
                 *	@param	_data	std::string contenant les données à écrire dans le buffer
                 *	@param	_size	La taille à extraire de la string.
                 *	@return			rien à retourner
                 */
                /**	@fn				put(const std::string& _data, size_t _size)
                        @lang			English
                        @brief			Write an std::string into the buffer performing an extraction of _size octets,
                                                        overloading the template function to avoid unprimitive type.
                        @param	_data	Data to write into the buffer.
                        @param	_size	Size to extract from the string.
                        @return			no return
                 */
                void put(const std::string& _data, size_t _size);


                /**	@fn				put(const void* _data, std::size_t _length)
                 *	@lang			French
                 *	@brief			Copie C-style de donnÈes sur le buffer
                 *	@param	_data	Pointeur sur le début des données à copier
                 *	@param	_length	Taille de la portion de mémoire à copier en octets.
                 *	@return			rien à retourner
                 */
                /**	@fn				put(const void* _data, std::size_t _length)
                        @lang			English
                        @brief			C-style copy into the buffer
                        @param	_data	Pointer on the first octet of the Data to write into the buffer.
                        @param	_size	Size to extract from memory.
                        @return			no return
                 */
                void put(const void *_data, std::size_t _length);

                /**	@fn				put_at_offset(T _data, boost::uint16_t_offset)
                 *	@lang			French
                 *	@brief			Ecrire une type primitif sur le buffer à un offset précis
                 *	@param	_data	Donnée à écrire
                 *	@param	_offset	Offset à partir duquel commencer à copier
                 *	@return			rien à retourner
                 */

                /**	@fn				put_at_offset(T _data, boost::uint16_t_offset)
                        @lang			English
                        @brief			Write a primitive type at the specified offset
                        @param	_data	Data to write.
                        @param	_offset	Where start to copy from.
                        @return			no return
                 */
                template<typename T>
                void put_at_offset(T _data, boost::uint16_t _offset)
                {
                    BOOST_ASSERT((sizeof (T) + _offset) <= m_size);
                    *((T*) (&((*m_data)[_offset]))) = data;
                }

                /**
                 *	@fn				put_at_offset(const void *data, std::size_t length, boost::uint16_toffset )
                 *	@lang			French
                 *	@brief			Copie C-style de donnÈes sur le buffer à un offset prÈcis
                 *	@param	_data	Pointeur sur le premier octets des donnée à écrire.
                 *	@param	_length	Taille des données à écrire
                 *	@param	_offset	A partir de quel octet copier les données.
                     $	@return			rien à retourner.
                 */
                /**	@fn				put_at_offset(const void *data, std::size_t length, boost::uint16_toffset )
                        @lang			English
                        @brief			C-style Writing at the specified offset
                        @param	_data	Pointer on the first octets of data to write.
                        @param	_length	Length of the data to copy.
                        @param	_offset	Where start to copy from.
                        @return			no return
                 */
                void put_at_offset(const void *_data, std::size_t _length, boost::uint16_t _offset);

                /**	@fn				T get()
                 *	@lang			French
                 *	@brief			On lit sur le buffer comme si c'Ètait un stream
                 *	@return			Les données dont le type à été spécifié.
                 */

                /**	@fn				T get()
                        @lang			English
                        @brief			Streaming read on the buffer.
                        @return			The data with the specified type.
                 */
                template<typename T>
                T get()
                {
                    BOOST_ASSERT((sizeof (T) + m_offset) <= m_size);
                    // T tmp = swap_data<T>::swap(*((T*)(&((*data_)[offset_]))));
                    T tmp = *((T*) (&((*m_data)[m_offset])));
                    m_offset += sizeof (T);
                    return tmp;
                }
				
				template < class T >
				void get( T& _data )
				{
					BOOST_ASSERT( (sizeof(T) + m_offset ) <= m_size );
					_data = *((T*) (&((*m_data)[m_offset])));
					m_offset += sizeof(T);
				}

				template< class T>
				void get( T& _data, std::size_t _length)
                {
                    BOOST_ASSERT( ( (sizeof(T) * _length) + m_offset ) <= m_size );
					memcpy(	&_data, &((*m_data)[m_offset]), _length );
                }

				/*template<>
				void get<we::RawData>( we::RawData& _data , std::size_t offsetFromStart )
                {
					BOOST_ASSERT((sizeof(boost::uint8_t) + offsetFromStart) <= m_size);
                    // T tmp = swap_data<T>::swap(*((T*)(&((*data_)[offset_]))));
					boost::uint8_t _length = (*m_data)[offsetFromStart];
					offsetFromStart += sizeof(boost::uint8_t);
					BOOST_ASSERT(( sizeof(unsigned char) * _length + offsetFromStart) <= m_size);
					int i = 0;
					for( i; i < _length ; i++ )
					{
						_data.push_back( (*m_data)[offsetFromStart + i] );
                    }
                }*/

                /**	@fn				get_at_offset(boost::uint16_toffset)
                        @lang			French
                        @brief			C-style Writing at the specified offset
                        @param	_data	Pointeur dans lequel écrire les données lu du buffer.
                        @param	_length	Longueur des données à lire.
                        @param	_offset	A partir de où commencer à copier.
                        @return			rien à retourner
                 */
                /**	@fn				get_at_offset(const void *data, std::size_t length, boost::uint16_toffset )
                        @lang			English
                        @brief			C-style reading at the specified offset on the buffer with the specified length
                        @param	_data	Pointer on the first octets of data to write to.
                        @param	_length	Length of the data to read.
                        @param	_offset	Where start to copy from.
                        @return			no return
                 */
                void get_at_offset(void *data, std::size_t length, boost::uint16_t _toffset);



            protected:

                boost::shared_ptr< std::vector<unsigned char> > m_data; /**	Pointer where insert data to send			*/
                boost::asio::const_buffer m_buffer; /**	Asio buffer keeping the adress of *m_data	*/
                std::size_t m_size; /**	Store the size of the buffer				*/
                int m_offset; /** Convenience to buffer streaming				*/
            };

        } //  namespace buffer
    } //  //	namespace network

#endif // SHARED_CONST_BUFFER_HPP
